across(.cols, .fns, ..., .names)Estadísticos, operaciones múltiples y resúmenes
Unidad 5
Las funciones que calculan medidas de tendencia central, de dispersión y de posición, que son parte integral del lenguaje R, como mean(), sd() o max(), se aplican sobre vectores o variables de un dataframes (internamente funcionan igual), utilizando estructuras de tidyverse como el summarise().
Asociadas al group_by(), o a la inclusión del argumento .by = dentro de summarise(), se obtiene estos resultados estratificados por categorías de variables cualitativas (las usamos para agrupar).
Esta tarea conlleva escribir una línea de código por cada variable.
La filosofía de trabajo de tidyverse se plantea nunca copiar y pegar más de dos veces el código escrito, pero cuando necesitamos realizar la misma operación en un conjunto de variables simultáneamente nos encontramos con este problema.
La solución, ofrecida dentro de dplyr, es un andamiaje que permite aplicar funciones y expresiones a varias columnas simultáneamente.
Es una forma de iteración, donde se repite la misma acción en diferentes objetos. En este caso los objetos serán columnas (variables) de la tabla de datos.
Las operaciones simultáneas pueden darse como transformación (dentro de un mutate()) o de resumen (dentro de un summarise())
Creación de múltiples columnas con mutate()
Resumiendo múltiples columnas con summarise()
La función across() es la encargada de dar soporte a estas operaciones múltiples (dplyr >= 1.0.0).
.cols = columnas a transformar
.fns = función o funciones para aplicar a cada columna de .cols
... = argumentos adicionales de las funciones especificadas anteriormente (ejemplo: na.rm = T)
.names = nombres de las columnas de salida. Aquí, {.col} es un marcador especial al que se le puede agregar el sufijo deseado.
Tomemos la siguiente tabla de datos ficticios (mostramos las primeras 4 observaciones):
# A tibble: 4 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 -0.560 1.22 -1.07 0.426
2 -0.230 0.360 -0.218 -0.295
3 1.56 0.401 -1.03 0.895
4 0.0705 0.111 -0.729 0.878
Supongamos que queremos calcular la media de cada variable…
Podríamos hacerlo repitiendo para cada variable
# A tibble: 1 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 0.0746 0.209 -0.425 0.322
Pero esto rompe la regla general que buscamos de nunca copiar y pegar más de dos veces…
Para solucionarlo aplicamos across() y realizamos el resumen simultáneo en una sola línea.
# A tibble: 1 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 0.0746 0.209 -0.425 0.322
Nótese que el primer argumento es el rango de nombres de variables y el segundo la función que aplicamos a todas ellas (nombres de funciones sin paréntesis).
El primer argumento de across() responde de la misma forma que la función select() y aplican también las funciones ayudantes de selección.
everything(): coincide con todas las variables.
group_cols(): seleccione todas las columnas de agrupación.
starts_with(): comienza con un prefijo.
ends_with(): termina con un sufijo.
contains(): contiene una cadena literal.
matches(): coincide con una expresión regular.
num_range(): coincide con un rango numérico como x01, x02, x03.
all_of(): coincide con nombres de variables en un vector de caracteres. Todos los nombres deben estar presentes; de lo contrario, se generará un error de fuera de límites.
any_of(): igual que all_of(), excepto que no se genera ningún error para los nombres que no existen.
where(): aplica una función a todas las variables y selecciona aquellas para las cuales la función regresa TRUE.
El argumento .cols también puede recibir construcciones booleanas utilizando los operadores conocidos como ! (negación) y conectores lógicos como & (AND) y | (OR) entre las funciones ayudantes de selección.
Por ejemplo:
Selecciona todas las columnas no numéricas, cuyo nombre comienza con “a”.
Hasta ahora vimos el ejemplo de aplicar una función simple como mean() a un grupo de variables.
Que sucede si entre los datos de esas variables hay valores NA?
Vamos a necesitar incorporar el argumento na.rm = TRUE a la función.
Donde lo hacemos dentro de un across()?
Supongamos que tenemos estos datos (mostramos algunas observaciones):
# A tibble: 4 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 1.56 -1.27 NA -0.473
2 -0.560 NA -1.05 -1.07
3 -0.230 1.22 0.238 -0.218
4 NA -0.446 1.29 -1.03
Vemos algunos valores NA entre las observaciones.
Si aplicamos el mismo código de across() anterior tendríamos como resultado:
# A tibble: 1 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 NA NA NA -0.703
Sería bueno que le pasaramos na.rm = TRUE a la función mean().
Existen dos formas sintácticas de hacerlo.
Una función estilo-purrr (tidyverse): ~ mean(.x, na.rm = TRUE)
Una función anónima (base): function(x) mean(x, na.rm = TRUE) ; o mejor en su forma de atajo: \(x) mean(x, na.rm = TRUE)
Para incorporar más de una función dentro de across() debemos incluirlas dentro de una lista [list()]
datos_na |>
summarise(
across(a:d, list(
media = \(x) mean(x, na.rm = TRUE),
n_na = \(x) sum(is.na(x))))
)# A tibble: 1 × 8
a_media a_n_na b_media b_n_na c_media c_n_na d_media d_n_na
<dbl> <int> <dbl> <int> <dbl> <int> <dbl> <int>
1 0.210 1 -0.293 1 0.161 2 -0.703 0
La lista contiene cada función a aplicar, bajo nombres definidos.
Observemos que los nombres de las variables resultado se componen del nombre de la columna, un guión bajo y el nombre definido de la función aplicada, para distinguir entre las múltiples funciones del across().
La estructura de estos nombres se pueden modificar con el argumento .names de across().
Los marcadores especiales para el nombre de columna es {.col} y para el nombre de la función definida es {.fn}.
Por ejemplo, podríamos invertir el orden predeterminado de los nombres del resumen.
Hasta ahora vimos como funciona la función across() dentro de un resumen (summarise) pero al comienzo también dijimos que se puede utilizar para transformaciones masivas de datos.
Para lograr esto la función se vincula con mutate() modificando las variables originales o bien creando nuevas variables si cambiamos su nombre con .names.
Aplicamos la función coalesce() para convertir los valores NA en ceros, transformando las variables originales.
Hacemos lo mismo pero cambiamos los nombres de las variables de salida del mutate() que van a coexistir con las originales.
# A tibble: 5 × 8
a b c d a_na_cero b_na_cero c_na_cero d_na_cero
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.56 -1.27 NA -0.473 1.56 -1.27 0 -0.473
2 -0.560 NA -1.05 -1.07 -0.560 0 -1.05 -1.07
3 -0.230 1.22 0.238 -0.218 -0.230 1.22 0.238 -0.218
4 NA -0.446 1.29 -1.03 0 -0.446 1.29 -1.03
5 0.0705 -0.687 NA -0.729 0.0705 -0.687 0 -0.729
En el caso de iteraciones similares para incluir dentro de la función filter() el paquete dplyr propone dos funciones específicas: if_any() e if_all().
En el primer caso, la función enmascara una repetición de OR lógicos y en la segunda una secuencia de AND lógicos.
# A tibble: 4 × 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 1.56 -1.27 NA -0.473
2 -0.560 NA -1.05 -1.07
3 NA -0.446 1.29 -1.03
4 0.0705 -0.687 NA -0.729
Es lo mismo que filter(is.na(a) | is.na(b) | is.na(c) | is.na(d))
# A tibble: 0 × 4
# ℹ 4 variables: a <dbl>, b <dbl>, c <dbl>, d <dbl>
Es lo mismo que filter(is.na(a) & is.na(b) & is.na(c) & is.na(d))
Las dos funciones de filtro trabajan con el mismo esquema que across(), por lo tanto se le puede aplicar una función o expresión de condición (debe devolver TRUE o FALSE)
El paquete rstatix simplifica el proceso de realizar análisis estadísticos complejos.
# A tibble: 1 × 8
.y. group1 group2 n1 n2 statistic df p
* <chr> <chr> <chr> <int> <int> <dbl> <dbl> <dbl>
1 Edad Mujer Varon 29 33 -0.871 59.6 0.387
Lo que nos permite conectar el resultado mediante tuberías hacia otras funciones del tidyverse, como select() o filter().
Tiene dos funciones de resumenes para variables cuantitativas y cualitativas:
# A tibble: 2 × 11
Sexo variable n min max median iqr mean sd se ci
<chr> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Mujer Edad 29 18 93 57 39 55.7 23.9 4.44 9.10
2 Varon Edad 33 19 99 63 41 61.1 25.1 4.37 8.90
# A tibble: 2 × 3
Sexo n prop
<chr> <int> <dbl>
1 Mujer 31 46.3
2 Varon 36 53.7
El paquete janitor tiene numerosas funciones de las cuales vamos a destacar la familia tabyl.
La función tabyl() permite crear tablas de frecuencia de una variable o tablas de contingencia para dos o más variables categóricas.
El paquete además tiene otras funciones que se enlazan con tabyl para personalizar los resultados en estas tablas (por ejemplo, agregar totales o representar frecuencias realtivas como porcentajes)
Se puede personalizar la tabla conectando por tuberías el agregado de otras funciones del paquete.
library(janitor)
datos |>
tabyl(Sexo) |>
adorn_totals(where = "row") |>
adorn_pct_formatting(digits = 2) Sexo n percent
Mujer 31 46.27%
Varon 36 53.73%
Total 67 100.00%
Incorporamos totales con adorn_totals() y configuramos los porcentajes con adorn_pct_formatting().
Este paquete permite crear tablas de resumen elegantes y personalizables que destacan las estadísticas descriptivas, resultados de modelos, y comparaciones de grupos.
Estas tablas son esenciales para reportes, publicaciones y presentaciones, asegurando que la información clave sea fácilmente comprensible.
Las salidas buscan producir estéticas compatibles con las tablas que se envían a publicar en la mayoría de las revistas científicas, así como también en otras publicaciones similares.
Un ejemplo de una tabla descriptiva con variables cuanti y cuali estratificada por Sexo.
Este paquete es similar a otras librerías como gt, huxtable, kableExtra, etc, que transforman las tablas y dataframes de R en salidas elegantes.
La documentación oficial dice “crear tablas flexibles y altamente personalizables en formatos de Microsoft Word, PowerPoint y HTML”.
La elección de este paquete sobre las otras opciones tiene que ver con la amplitud de opciones en la compatibilidad de los formatos de salida.
Las funciones del paquete se integran facilmente con Quarto
Una tablita que creamos anteriormente con freq_table() de rstatix fue:
# A tibble: 2 × 3
Sexo n prop
<chr> <int> <dbl>
1 Mujer 31 46.3
2 Varon 36 53.7
La salida tradicional de consola se visualiza estéticamente fea.
Con flextable podemos configurar esa misma tabla, para mejorar su presentación:
library(flextable)
datos |> freq_table(Sexo) |>
flextable() |>
fontsize(size = 30, part = "all") |>
align(align = "center", part = "all") |>
line_spacing(space = 2, part = "all") |>
padding(padding = 6, part = "header") |>
set_header_labels(n = "Frecuencia", prop = "%") |>
theme_zebra()Sexo | Frecuencia | % |
|---|---|---|
Mujer | 31 | 46.3 |
Varon | 36 | 53.7 |
Instituto Nacional de Epidemiología